import format.amf.Value;
import format.flv.Data;
import hxvid.events.ConnectionEvent;
import hxvid.events.StreamEvent;
import hxvid.IApplication;
import hxvid.Client;
import hxvid.Logger;
import hxvid.Rtmp;
import hxvid.Commands;
import hxvid.Data;
import hxvid.Server;
import hxvid.SharedObject;

typedef User=
{
	var id:Int;
	var name:String;
	var streaming:Bool;
}

class Application implements IApplication
{
	public var config : ApplicationConfig;
	public var users : Hash<User>;
	public var clients : Hash<Client>;
	
	private var server:Server;
	private var clientID:Int;
	private var logger:Logger;
	
	public function new(server:Server, config:ApplicationConfig)
	{
		this.server = server;
		this.config = config;
		
		this.clientID = 0;
		this.clients = new Hash();
		this.users = new Hash();
		logger = new Logger(config.logFile);
		logger.id = "streaming/Application";
		logger.info("started version:"+config.version);
		//server.addEventListener(ServerEvent.CLIENT_CONNECT, onBeforeConnect);
	}
	public function onConnect(client:Client, obj:ConnectionObject, ?arg:Dynamic=null):Void
	{
		logger.info("client:"+ client.ip);
		client.addEventListener(ConnectionEvent.CONNECTED, onConnected);
		client.addEventListener(ConnectionEvent.DISCONNECTED, onClientDisconnect);
		client.addEventListener(StreamEvent.STREAM_PUBLISH, onStreamPublish);
		client.addEventListener(StreamEvent.STREAM_CLOSE, onStreamClose);
		client.addEventListener(StreamEvent.STREAM_PLAY, onStreamPlay);
		//client.addEventListener(ConnectionEvent.DISCONNECTED, onClientDisconnect);
		//clients.push(client);
		client.addCommands(
		[
			["login", this.login, T.Null, T.String ],
			["textInput", this.textInput, T.Null, T.String ],
			["getUsers", this.getUsers, T.Null ]
		]);
		client.accept();
	}
	function onConnected(event:ConnectionEvent):Void
	{
		logger.info("client:" + event.target.ip + " has received NetConnection.Connect.Success)");
	}
	function login(cinf:CommandInfos, _ ,name:String):Void
	{	
		var client:Client = cinf.c;
		logger.info("client:"+client.ip +", with name:"+ name);
		
		if(users.exists(name))
		{
			client.sendResult(cinf, false);
		}
		else
		{
			client.name = name;
			client.id = clientID++;
			var user = {id:client.id, name:client.name, streaming:false};
			users.set(name, user);
			clients.set(name, client);
			client.sendResult(cinf, true);
			for (c in clients)
			{
				c.send2("userJoin", Date.now(), user);
			}
		}
	}
	function textInput(cinf:CommandInfos, _ , text:String):Void
	{
		var client:Client = cinf.c;
		logger.info("client:"+client.ip +", text:"+ text);
		var user = users.get(client.name);
		for (c in clients)
		{
			c.send3("textInput", Date.now(), user, text);
		}
	}
	function onStreamPublish(event:StreamEvent)
	{
		var client = event.target;
		var stream = event.data;
		logger.info("client:"+client.ip +", stream id:"+ stream.id+", name:"+stream.name);
		
		var user = users.get(client.name);
		user.streaming = true;
		for (c in clients)
		{
			c.send1("updateStreamersList", user);
		}
	}
	function onStreamClose(event:StreamEvent)
	{
		var client = event.target;
		var stream = event.data;
		logger.info("client:"+client.ip +", streamId:"+ stream.id+", streamName:"+stream.name);
		
		var user = users.get(client.name);
		if(client == clients.get(stream.name))
		{
			user.streaming = false;
			for (c in clients)
			{
				c.send1("updateStreamersList", user);
			}
		}
		else
		{
			clients.get(stream.name).send1("updateWatchersList", user);
		}
	}
	function onStreamPlay(event:StreamEvent)
	{
		var client = event.target;
		var stream = event.data;
		var user = users.get(client.name);
		logger.info("client:"+client.ip +", streamId:"+ stream.id+", streamName:"+stream.name);
		if(clients.exists(stream.name))
		{
			clients.get(stream.name).send1("updateWatchersList", user);
		}
	}
	function getUsers(cinf:CommandInfos, _ ):Void
	{
		var client:Client = cinf.c;
		logger.info("client:"+client.ip);
		var out=new Array();
		for (c in users)
				out.push(c);
		client.sendResult(cinf, out);
	}
	public function onClientDisconnect(event:ConnectionEvent)
	{
		var client = event.target;
		if(client!=null && client.id!=null && client.name!="")
		{
			var user = users.get(client.name);
			logger.info("version:"+config.version+", client:" +client.ip+", id:"+client.id + ", name:" + client.name);
			clients.remove(client.name);
			users.remove(client.name);
			for (c in clients)
			{
				c.send2("userLeave", Date.now(), user);
			}
		}
	}
	public function cleanup()
	{
		for(client in clients)
		{
			client.removeEventListener(ConnectionEvent.CONNECTED, onConnected);
			client.removeEventListener(ConnectionEvent.DISCONNECTED, onClientDisconnect);
			client.removeEventListener(StreamEvent.STREAM_PUBLISH, onStreamPublish);
			client.removeEventListener(StreamEvent.STREAM_CLOSE, onStreamClose);
			client.removeEventListener(StreamEvent.STREAM_PLAY, onStreamPlay);
			//client.removeEventListener(ConnectionEvent.DISCONNECTED, onClientDisconnect);
			server.stopClient(client.socket);
		}
		config=null;
		users=new Hash();
		clients = new Hash();
		server=null;
		logger=null;
	}
}